/* picoc interactive debugger */
#include "interpreter.h"

#define BREAKPOINT_HASH(p) (((unsigned long)(p)->fileName) ^ (((p)->line << 16) | ((p)->characterPos << 16)))

#ifdef DEBUGGER
/* initialize the debugger by clearing the breakpoint table */
void DebugInit(Interpreter *pc)
{
    TableInitTable(&pc->breakpointTable, &pc->breakpointHashTable[0],
        BREAKPOINT_TABLE_SIZE, true);
    pc->breakpointCount = 0;
}

/* free the contents of the breakpoint table */
void DebugCleanup(Interpreter *pc)
{
    struct TableEntry *Entry;
    struct TableEntry *NextEntry;
    int Count;

    for (Count = 0; Count < pc->breakpointTable.size; Count++) {
        for (Entry = pc->breakpointHashTable[Count]; Entry != NULL;
                Entry = NextEntry) {
            NextEntry = Entry->next;
            HeapFreeMem(pc, Entry);
        }
    }
}

/* search the table for a breakpoint */
static struct TableEntry *DebugTableSearchBreakpoint(struct ParseState *parser,
    int *AddAt)
{
    struct TableEntry *Entry;
    Interpreter *pc = parser->pc;
    int HashValue = BREAKPOINT_HASH(parser) % pc->breakpointTable.size;

    for (Entry = pc->breakpointHashTable[HashValue];
            Entry != NULL; Entry = Entry->next) {
        if (Entry->p.b.fileName == parser->fileName &&
                Entry->p.b.line == parser->line &&
                Entry->p.b.characterPos == parser->characterPos)
            return Entry;   /* found */
    }

    *AddAt = HashValue;    /* didn't find it in the chain */
    return NULL;
}

/* set a breakpoint in the table */
void DebugSetBreakpoint(struct ParseState *parser)
{
    int AddAt;
    struct TableEntry *FoundEntry = DebugTableSearchBreakpoint(parser, &AddAt);
    Interpreter *pc = parser->pc;

    if (FoundEntry == NULL) {
        /* add it to the table */
        struct TableEntry *NewEntry = HeapAllocMem(pc, sizeof(*NewEntry));
        if (NewEntry == NULL)
            ProgramFailNoParser(pc, "(DebugSetBreakpoint) out of memory");

        NewEntry->p.b.fileName = parser->fileName;
        NewEntry->p.b.line = parser->line;
        NewEntry->p.b.characterPos = parser->characterPos;
        NewEntry->next = pc->breakpointHashTable[AddAt];
        pc->breakpointHashTable[AddAt] = NewEntry;
        pc->breakpointCount++;
    }
}

/* delete a breakpoint from the hash table */
int DebugClearBreakpoint(struct ParseState *parser)
{
    struct TableEntry **EntryPtr;
    Interpreter *pc = parser->pc;
    int HashValue = BREAKPOINT_HASH(parser) % pc->breakpointTable.size;

    for (EntryPtr = &pc->breakpointHashTable[HashValue];
            *EntryPtr != NULL; EntryPtr = &(*EntryPtr)->next) {
        struct TableEntry *DeleteEntry = *EntryPtr;
        if (DeleteEntry->p.b.fileName == parser->fileName &&
                DeleteEntry->p.b.line == parser->line &&
                DeleteEntry->p.b.characterPos == parser->characterPos) {
            *EntryPtr = DeleteEntry->next;
            HeapFreeMem(pc, DeleteEntry);
            pc->breakpointCount--;

            return true;
        }
    }

    return false;
}

/* before we run a statement, check if there's anything we have to
    do with the debugger here */
void DebugCheckStatement(struct ParseState *parser)
{
    int DoBreak = false;
    int AddAt;
    Interpreter *pc = parser->pc;

    /* has the user manually pressed break? */
    if (pc->debugManualBreak) {
        PlatformPrintf(pc->cStdOut, "break\n");
        DoBreak = true;
        pc->debugManualBreak = false;
    }

    /* is this a breakpoint location? */
    if (parser->pc->breakpointCount != 0 &&
            DebugTableSearchBreakpoint(parser, &AddAt) != NULL)
        DoBreak = true;

    /* handle a break */
    if (DoBreak) {
        PlatformPrintf(pc->cStdOut, "Handling a break\n");
        PicocParseInteractiveNoStartPrompt(pc, false);
    }
}

void DebugStep(void)
{
}
#endif /* DEBUGGER */
